﻿/**
 * This class seemed to be best pick for Cubic  - Quadratic curve convesation.
 * Hopefuly it's not illegal to use this class!
 * Thank you Alex Uhlmann, nice work!
 */
package com.robertpenner.bezier {
	import flash.display.Sprite;
	import flash.geom.Point;

	/**
	 * Class for drawing cubic bezier curves. 
	 * Adapted from bezier_draw_cubic.as and drawing_api_core_extensions.as by Alex Uhlmann. 
	 * Approved by Robert Penner.
	 * @author Robert Penner, Alex Uhlmann
	 * @version 0.4 ALPHA
	 * -note that line 92 this.mc.moveTo (p1.x, p1.y); is commented out.
	 * -ported to AS3
	 */
	public class CubicCurve {
		private var _xpen:Number;
		private var _ypen:Number;
		//private var mc:Sprite;
		
		private var outputData:Array = null;
		
		public function CubicCurve() {
			//this.mc = mc;
		}

		private function intersect2Lines(p1:Object, p2:Object, p3:Object, p4:Object):Object {
			var x1:Number = p1.x; 
			var y1:Number = p1.y;
			var x4:Number = p4.x; 
			var y4:Number = p4.y;
		
			var dx1:Number = p2.x - x1;
			var dx2:Number = p3.x - x4;
			if (!(dx1 || dx2)) {
				return NaN;
			}
		
			var m1:Number = (p2.y - y1) / dx1;
			var m2:Number = (p3.y - y4) / dx2;
		
			if (!dx1) {			// infinity
				return { x:x1, y:m2 * (x1 - x4) + y4 };
			} else if (!dx2) {	// infinity
				return { x:x4, y:m1 * (x4 - x1) + y1 };
			}
			var xInt:Number = (-m2 * x4 + y4 + m1 * x1 - y1) / (m1 - m2);
			var yInt:Number = m1 * (xInt - x1) + y1;
			return { x:xInt, y:yInt };
		}
	
		private function midLine(a:Object, b:Object):Object {		
			return { x:(a.x + b.x)/2, y:(a.y + b.y)/2 };
		}
	
		private function bezierSplit(p0:Object, p1:Object, p2:Object, p3:Object):Object {		
			var m:Function = this.midLine;
			var p01:Object = m(p0, p1);
			var p12:Object = m(p1, p2);
			var p23:Object = m(p2, p3);
			var p02:Object = m(p01, p12);
			var p13:Object = m(p12, p23);
			var p03:Object = m(p02, p13);
			return {
				b0:{a:p0,  b:p01, c:p02, d:p03},
				b1:{a:p03, b:p13, c:p23, d:p3 }  
			};
		}

		private function _cBez(a:Object, b:Object, c:Object, d:Object, k:Number):void {	
			// find intersection between bezier arms
			var s:Object = intersect2Lines (a, b, c, d);
			// find distance between the midpoints
			var dx:Number = (a.x + d.x + s.x * 4 - (b.x + c.x) * 3) * .125;
			var dy:Number = (a.y + d.y + s.y * 4 - (b.y + c.y) * 3) * .125;
			// split curve if the quadratic isn't close enough
			if (dx * dx + dy * dy > k) {
				var halves:Object = this.bezierSplit (a, b, c, d);
				var b0:Object = halves.b0; var b1:Object = halves.b1;
				// recursive call to subdivide curve
				_cBez (a, b0.b, b0.c, b0.d, k);
				_cBez (b1.a,  b1.b, b1.c, d, k);
			} else {
				outputData.push([s.x - _xpen, s.y - _ypen, d.x - _xpen, d.y - _ypen]);
				//mc.graphics.lineStyle(1, Math.floor(Math.random() * 0xFFFFFF));
				//mc.graphics.curveTo (s.x, s.y, d.x, d.y);
				_xpen = d.x;
				_ypen = d.y;
			}
		}
	
		private function drawBezierPts(p1:Object, p2:Object, p3:Object, p4:Object, tolerance:Number = -1):void {		
			if (tolerance == -1) {
				tolerance = 5;
			}
			//mc.graphics.moveTo (p1.x, p1.y);
			_xpen = p1.x;
			_ypen = p1.y;
			_cBez (p1, p2, p3, p4, tolerance*tolerance);
		}

		private function curveToCubic(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number, tolerance:Number = -1):void {		
			if (tolerance == -1) {
				tolerance = 5;
			}
			_cBez ({x:this._xpen, y:this._ypen}, {x:x1, y:y1}, {x:x2, y:y2}, {x:x3, y:y3}, tolerance * tolerance);
		}

		private function curveToCubicPts(p1:Object, p2:Object, p3:Object, tolerance:Number = -1):void {
			if (tolerance == -1) {
				tolerance = 5;
			}
			_cBez( { x:_xpen, y:_ypen }, p1, p2, p3, tolerance * tolerance);
		}
		
		public function drawBezier(x1:Number, y1:Number, x2:Number, y2:Number, x3:Number, y3:Number, x4:Number, y4:Number, tolerance:Number = -1):Array {	
			outputData = new Array()
			drawBezierPts ({x:x1, y:y1}, {x:x2, y:y2}, {x:x3, y:y3}, {x:x4, y:y4}, tolerance);
			return outputData;
		}
	}
}